package org.hawk.gwt.ppc.parser.impl;

import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.lookupNext;
import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.lookupNextNonComment;
import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.MODIFIERS;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.hawk.gwt.ppc.parser.ClassDescriptor;
import org.hawk.gwt.ppc.parser.ClassDescriptor.ClassDescriptorType;
import org.hawk.gwt.ppc.parser.CastExpressionDescriptor;
import org.hawk.gwt.ppc.parser.CommaExpressionDescriptor;
import org.hawk.gwt.ppc.parser.Descriptor;
import org.hawk.gwt.ppc.parser.DescriptorVisitor;
import org.hawk.gwt.ppc.parser.ExpressionDescriptor;
import org.hawk.gwt.ppc.parser.InitializerExpressionDescriptor;
import org.hawk.gwt.ppc.parser.JavaKeywords;
import org.hawk.gwt.ppc.parser.MethodCallDescriptor;
import org.hawk.gwt.ppc.parser.NameExpressionDescriptor;
import org.hawk.gwt.ppc.parser.NewOperatorExpressionDescriptor;
import org.hawk.gwt.ppc.parser.OperatorExpressionDescriptor;
import org.hawk.gwt.ppc.parser.PreprocessorParseException;
import org.hawk.gwt.ppc.parser.TernaryExpressionDescriptor;
import org.hawk.gwt.ppc.parser.Token;
import org.hawk.gwt.ppc.utils.PreprocessorException;

/**
 * Parses line expression (terminated with ';').
 * 
 * @author alex.bereznevatiy@gmail.com
 *
 */
final class LineExpressionDescriptorParser implements DescriptorParser {

	private static final Set<String> BINARY_OPERATORS;
	
	static {
		Set<String> result = new HashSet<String>();
		
		result.add("+");
		result.add("-");
		result.add("*");
		result.add("/");
		result.add("%");
		result.add("=");
		result.add(">>");
		result.add("<<");
		result.add(">>>");
		result.add(">");
		result.add("<");
		result.add(">=");
		result.add("<=");
		result.add("==");
		result.add("!=");
		result.add("&");
		result.add("^");
		result.add("|");
		result.add("instanceof");
		result.add("&&");
		result.add("||");
		result.add("*=");
		result.add("/=");
		result.add("+=");
		result.add("-=");
		result.add("%=");
		result.add("<<=");
		result.add(">>=");
		result.add(">>>=");
		result.add("&=");
		result.add("^=");
		result.add("|=");
		
		BINARY_OPERATORS = Collections.unmodifiableSet(result);
	}
	
	private static final Map<String, Integer> OPERATORS_PRIORITIES;
	
	static {
		Map<String, Integer> result = new HashMap<String, Integer>();
		
		result.put("[", 1);
		result.put("(", 1);
		result.put(".", 1);
		
		result.put("++", 2);
		result.put("--", 2);
		result.put("~", 2);
		result.put("!", 2);
		result.put("new", 2);
		
		result.put("*", 3);
		result.put("/", 3);
		result.put("%", 3);
		
		result.put("+", 4);
		result.put("-", 4);

		result.put("<<", 5);
		result.put(">>", 5);
		result.put(">>>", 5);
		
		result.put(">", 6);
		result.put(">=", 6);
		result.put("<", 6);
		result.put("<=", 6);
		result.put("instanceof", 6);
		
		result.put("==", 7);
		result.put("!=", 7);
		
		result.put("&", 8);
		
		result.put("^", 9);
		
		result.put("|", 10);
		
		result.put("&&", 11);
		
		result.put("||", 12);
		
		result.put("?", 13);
		
		result.put("=", 14);
		result.put("*=", 14);
		result.put("/=", 14);
		result.put("+=", 14);
		result.put("-=", 14);
		result.put("%=", 14);
		result.put("<<=", 14);
		result.put(">>=", 14);
		result.put(">>>=", 14);
		result.put("&=", 14);
		result.put("^=", 14);
		result.put("|=", 14);
		
		result.put(",", 15);
		
		OPERATORS_PRIORITIES = Collections.unmodifiableMap(result);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.parser.impl.DescriptorParser#accepts(org.hawk.gwt.ppc.parser.Token)
	 */
	public boolean accepts(Token token) {
		return !token.equalsTo('}') && !DescriptorType.BLOCK_EXPRESSION.accepts(token) && 
				!DescriptorType.KEYWORD_EXPRESSION.accepts(token);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.parser.impl.DescriptorParser#parse(org.hawk.gwt.ppc.parser.impl.TokenBasedJavaParser, org.hawk.gwt.ppc.parser.impl.Tokenizer, java.lang.Object[])
	 */
	public Descriptor parse(TokenBasedJavaParser parser, TokenSource tokenizer,
			Object... params) throws PreprocessorException {
		List<Token> nonCommentTokens = new LinkedList<Token>();
		int deep = 0;
		boolean allowEof = false, checkModifiers = true;
		if (params.length > 0) {
			allowEof = params[0] != null && (Boolean)params[0];
		}
		
		while (deep > 0 || (!tokenizer.isEof() && !tokenizer.current().equalsTo(';'))) {
			Token tok = tokenizer.current();
			if (tok.isComment()) {
				lookupNext(tokenizer, allowEof);
				continue;
			}
			if (checkModifiers) {
				checkModifiers = false;
				if (tok.equalsTo('@') || (tok.isWord() && (tok.equalsTo(JavaKeywords.CLASS) || MODIFIERS.contains(tok.toString())))) {
					Descriptor result = tryParseClass(nonCommentTokens, parser, tokenizer);
					if (result != null) {
						return result;
					}
				}
			}
			if (tok.equalsTo('{')) {
				deep++;
			} else if (tok.equalsTo('}')) {
				deep--;
			}
			if (tok.equalsTo(':') && nonCommentTokens.size() == 1 && nonCommentTokens.get(0).isWord()) {
				@SuppressWarnings("unchecked")
				List<String> labels = params.length > 1 ? (List<String>)params[1] : new ArrayList<String>();
				labels.add(nonCommentTokens.get(0).toString());
				return parseLabeledExpression(labels, parser, tokenizer);
			}
			nonCommentTokens.add(tokenizer.current());
			lookupNext(tokenizer, allowEof);
		}
		lookupNext(tokenizer, allowEof);
		return parse(parser, nonCommentTokens);
	}

	private Descriptor tryParseClass(List<Token> nonCommentTokens,
			TokenBasedJavaParser parser, TokenSource tokenizer) throws PreprocessorException {
		while (tokenizer.current().equalsTo('@')) {
			nonCommentTokens.add(tokenizer.current());
			lookupNext(tokenizer, false);
			if (!tokenizer.current().isWord()) {
				throw new PreprocessorParseException(tokenizer.current());
			}
			nonCommentTokens.add(tokenizer.current());
			lookupNextNonComment(tokenizer, false);
			if (tokenizer.current().equalsTo('(')) {
				int deep = 0;
				while(deep > 0) {
					nonCommentTokens.add(tokenizer.current());
					lookupNextNonComment(tokenizer, false);
					if (tokenizer.current().equalsTo('(')) {
						deep++;
					} else if (tokenizer.current().equalsTo(')')) {
						deep--;
					}
				}
				nonCommentTokens.add(tokenizer.current());
				lookupNextNonComment(tokenizer, false);
			}
		}
		while(MODIFIERS.contains(tokenizer.current().toString())) {
			nonCommentTokens.add(tokenizer.current());
			lookupNextNonComment(tokenizer, false);
		}
		if (!tokenizer.current().equalsTo(JavaKeywords.CLASS)) {
			return null;
		}
		
		while (!tokenizer.current().equalsTo('{')) {
			nonCommentTokens.add(tokenizer.current());
			lookupNextNonComment(tokenizer, false);
		}
		nonCommentTokens.add(tokenizer.current());
		int deep = 1;
		while (deep > 0) {
			lookupNextNonComment(tokenizer, false);
			nonCommentTokens.add(tokenizer.current());
			if (tokenizer.current().equalsTo('{')) {
				deep++;
			} else if (tokenizer.current().equalsTo('}')) {
				deep--;
			}
		}
		lookupNextNonComment(tokenizer, false);
		TokenSource iterator = new IteratorTokenSource(nonCommentTokens.iterator());
		iterator.next();
		return DescriptorType.CLASS.parse(parser, iterator);
	}

	private Descriptor parseLabeledExpression(List<String> labels,
			TokenBasedJavaParser parser, TokenSource tokenizer) throws PreprocessorException {
		lookupNextNonComment(tokenizer, false);
		Token token = tokenizer.current();
		if (DescriptorType.BLOCK.accepts(token)){
			return DescriptorType.BLOCK.parse(parser, tokenizer, Collections.emptyList(), Collections.unmodifiableList(labels));
		}
		if (DescriptorType.BLOCK_EXPRESSION.accepts(token)){
			return DescriptorType.BLOCK_EXPRESSION.parse(parser, tokenizer, Collections.unmodifiableList(labels));
		}
		if (DescriptorType.KEYWORD_EXPRESSION.accepts(token)){
			return DescriptorType.KEYWORD_EXPRESSION.parse(parser, tokenizer, Collections.unmodifiableList(labels));
		}
		return DescriptorType.LINE_EXPRESSION.parse(parser, tokenizer, false, labels);
	}

	private AbstractDescriptor parse(TokenBasedJavaParser parser, List<Token> tokens) throws PreprocessorException {
		if (tokens.isEmpty()) {
			return new EmptyExpression();
		}
		Deque<AbstractDescriptor> descriptorsStack = new LinkedList<AbstractDescriptor>();
		
		ListIterator<Token> iterator = tokens.listIterator();
		
		if (ckeckForVarDeclaration(tokens)) {
			TokenSource source = new IteratorTokenSource(iterator);
			source.next();
			AbstractDescriptor var = (AbstractDescriptor) DescriptorType.VARIABLE.parse(parser, source);
			if (source.isEof() || source.current().equalsTo(';')) {
				return var;
			}
			if (source.current().equalsTo('=')) {
				OperatorExpression result = new OperatorExpression(source.current());
				result.leftOperand = (ExpressionDescriptor) var;
				setParent(result.leftOperand, result);
				descriptorsStack.add(result);
			} else {
				throw new PreprocessorParseException(source.current());
			}
		}
		
		parse(descriptorsStack, iterator, parser);
		AbstractDescriptor result = descriptorsStack.removeLast();
		if (!descriptorsStack.isEmpty()) {
			if (descriptorsStack.size() == 1 && descriptorsStack.getFirst() instanceof CommaExpression) {
				CommaExpression comma = (CommaExpression) descriptorsStack.getFirst();
				comma.expressions.add((ExpressionDescriptor) result);
				setParent((ExpressionDescriptor) result, comma);
				result = comma;
			} else {
				checkStack(iterator, descriptorsStack, result);
				if (descriptorsStack.size() != 1) {
					throw new PreprocessorParseException("Syntax error: ", tokens.get(0));
				}
				return descriptorsStack.getLast();
			}
		}
		return result;
	}

	private boolean ckeckForVarDeclaration(List<Token> tokens) {
		ListIterator<Token> iterator = tokens.listIterator();
		Token tok = iterator.next();
		if (tok.equalsTo('@') || tok.equalsTo(JavaKeywords.FINAL)) {
			return true;
		}
		if (!tok.isWord() || !iterator.hasNext() || tok.equalsTo(JavaKeywords.NEW)) {
			return false;
		}
		boolean flag = false;
		while (iterator.hasNext()) {
			tok = iterator.next();
			if (flag) {
				flag = false;
				if (!tok.isWord() || tok.equalsTo(JavaKeywords.NEW)) {
					break;
				}
			} else {
				flag = true;
				if (!tok.equalsTo('.')) {
					break;
				}
			}
		}

		if (!flag) {
			return false;
		}
		if (!iterator.hasNext() || !tok.equalsTo('<')) {
			return flag && checkArray(tok, iterator);
		}
		int deep = 1;
		boolean canFollowQuestion = true;
		while (iterator.hasNext() && deep > 0) {
			tok = iterator.next();
			if (tok.equalsTo('<')) {
				canFollowQuestion = true;
				deep++;
			} else if (tok.equalsTo('>')) {
				canFollowQuestion = false;
				deep--;
			} else if (tok.equalsTo('?')) {
				return canFollowQuestion;
			} else if (tok.equalsTo(',')) {
				canFollowQuestion = true;
			} else if (tok.equalsTo(">>")) {
				canFollowQuestion = false;
				deep -= 2;
			} else if (tok.equalsTo(">>>")) {
				canFollowQuestion = false;
				deep -= 3;
			} else if (!tok.isWord()) { 
				return false;
			} else {
				canFollowQuestion = false;
			}
		}
		return iterator.hasNext() && checkArray(iterator.next(), iterator);
	}

	private boolean checkArray(Token current, ListIterator<Token> iterator) {
		while (current.equalsTo('[')) {
			if (!iterator.hasNext()) {
				return false;
			}
			if (!iterator.next().equalsTo(']')) {
				return false;
			}
			if (!iterator.hasNext()) {
				return false;
			}
			current = iterator.next();
		}
		return current.isWord() && !current.equalsTo(JavaKeywords.INSTANCEOF);
	}

	private void parse(Deque<AbstractDescriptor> descriptorsStack,
			ListIterator<Token> iterator, TokenBasedJavaParser parser) throws PreprocessorException {
		if (!iterator.hasNext()) {
			return;
		}
		Token tok = iterator.next();
		if (tok.isDigitLiteral() || tok.isStringLiteral() || tok.isCharacterLiteral()) {
			LiteralExpressionDescriptorImpl result = new LiteralExpressionDescriptorImpl(tok);
			checkStack(iterator, descriptorsStack, result);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.equalsTo(',')) {
			parseComma(descriptorsStack, tok);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.equalsTo('(')) {
			parseSubexprOrCast(tok, descriptorsStack, iterator, parser);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.equalsTo('{')) {
			iterator.previous();
			TokenSource source = new IteratorTokenSource(iterator);
			source.next();
			descriptorsStack.add((AbstractDescriptor)parseInitializer(parser, source, iterator, descriptorsStack));
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.isWord() && tok.equalsTo(JavaKeywords.NEW)) {
			parseNewExpression(tok, parser, descriptorsStack, iterator);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.isWord() && tok.equalsTo(JavaKeywords.INSTANCEOF)) {
			parseInstanceof(descriptorsStack, iterator, parser, tok);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.isWord()) {
			if (iterator.hasNext()) {
				Token bracket = iterator.next();
				if (bracket.equalsTo('(')) {
					parseMethodCall(tok, iterator, descriptorsStack, parser);
					parse(descriptorsStack, iterator, parser);
					return;
				}
				iterator.previous();
			}
			
			parseName(descriptorsStack, iterator, tok);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.equalsTo("++") || tok.equalsTo("--") || tok.equalsTo('~') || tok.equalsTo('!')) {
			parseUnary(descriptorsStack, tok);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.equalsTo('[')) {
			parseSubexpression(parser, descriptorsStack, iterator, ']', '[');
			OperatorExpression result = new OperatorExpression(tok);
			result.rightOperand = (ExpressionDescriptor) descriptorsStack.removeLast();
			result.leftOperand = (ExpressionDescriptor) descriptorsStack.removeLast();
			setParent(result.rightOperand, result);
			setParent(result.leftOperand, result);
			checkStack(iterator, descriptorsStack, result);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if ((tok.equalsTo('+') || tok.equalsTo('-')) && 
			(descriptorsStack.isEmpty() || isIncompleteOperator(descriptorsStack.getLast()))) {
			descriptorsStack.add(new OperatorExpression(tok));
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.equalsTo('.')) {
			parsePropertyOrMethod(descriptorsStack, iterator, tok, parser);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		if (tok.equalsTo('?')) {
			if (descriptorsStack.isEmpty()) {
				throw new PreprocessorParseException(tok);
			}
			TernaryOperatorExpression result = new TernaryOperatorExpression();
			result.condition = (ExpressionDescriptor)descriptorsStack.removeLast();
			setParent(result.condition, result);
			
			parseSubexpression(parser, descriptorsStack, iterator, ':', '?');
			result.trueExpression = (ExpressionDescriptor)descriptorsStack.removeLast();
			setParent(result.trueExpression, result);
			descriptorsStack.add(result);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		String binary = tok.toString();
		if (BINARY_OPERATORS.contains(binary)) {
			OperatorExpression result = new OperatorExpression(tok);
			result.leftOperand = (ExpressionDescriptor)descriptorsStack.removeLast();
			setParent(result.leftOperand, result);
			descriptorsStack.add(result);
			parse(descriptorsStack, iterator, parser);
			return;
		}
		
		throw new PreprocessorParseException("Unexpected token", tok);
	}

	private void parseName(Deque<AbstractDescriptor> descriptorsStack,
			ListIterator<Token> iterator, Token start) {
		StringBuilder name = new StringBuilder();
		start.writeTo(name);
		Token end = start;
		if (iterator.hasNext()) {
			while (iterator.next().equalsTo('[')) {
				if (!iterator.hasNext()) {
					break;
				}
				Token next;
				if (!(next = iterator.next()).equalsTo(']')) {
					iterator.previous();
					break;
				}
				end = next;
				name.append("[]");
				if (!iterator.hasNext()) {
					NameExpression nameExpression = new NameExpression(start, end, name.toString());
					checkStack(iterator, descriptorsStack, nameExpression);
					return;
				}
			}
			iterator.previous();
		}
		
		NameExpression nameExpression = new NameExpression(start, end, name.toString());
		checkStack(iterator, descriptorsStack, nameExpression);
	}

	private void parseInstanceof(Deque<AbstractDescriptor> descriptorsStack, ListIterator<Token> iterator, 
			TokenBasedJavaParser parser, Token operator) throws PreprocessorException {
		OperatorExpression result = new OperatorExpression(operator);
		result.leftOperand = (ExpressionDescriptor)descriptorsStack.removeLast();
		setParent(result.leftOperand, result);
		TokenSource tokenizer = new IteratorTokenSource(iterator);
		Token start = tokenizer.next();
		StringBuilder type = new StringBuilder();
		DescriptorParserUtils.readTypeName(type, tokenizer, true);
		Token end = iterator.previous();
		iterator.next();
		result.rightOperand = new NameExpression(start, end, type.toString());
		setParent(result.rightOperand, result);
		descriptorsStack.add(result);
	}

	private void parseComma(Deque<AbstractDescriptor> descriptorsStack,
			Token tok) throws PreprocessorParseException {
		if (descriptorsStack.isEmpty()) {
			throw new PreprocessorParseException(tok);
		}
		
		
		if (descriptorsStack.getFirst() instanceof CommaExpression) {
			if (descriptorsStack.size() != 2) {
				throw new PreprocessorParseException(tok);
			}
			ExpressionDescriptor expr = (ExpressionDescriptor) descriptorsStack.removeLast();
			CommaExpression comma = (CommaExpression) descriptorsStack.getFirst();
			comma.expressions.add(expr);
			setParent(expr, comma);
		} else {
			ExpressionDescriptor expr = (ExpressionDescriptor) descriptorsStack.removeLast();
			CommaExpression comma = new CommaExpression();
			comma.expressions.add(expr);
			descriptorsStack.add(comma);
			setParent(expr, comma);
		}
	}

	private void parsePropertyOrMethod(Deque<AbstractDescriptor> descriptorsStack, 
			ListIterator<Token> iterator, Token tok, TokenBasedJavaParser parser) throws PreprocessorException {
		if (!iterator.hasNext()) {
			throw new PreprocessorParseException(tok);
		}
		Token next = iterator.next();
		if (next.equalsTo('<')) {
			parseMethodCall(next, iterator, descriptorsStack, parser);
			return;
		}
		if (next.isWord() && !next.equalsTo(JavaKeywords.NEW)) {
			Token nextOperator = null;
			if (iterator.hasNext() && (nextOperator = iterator.next()).equalsTo('(')) {
				parseMethodCall(next, iterator, descriptorsStack, parser);
				return;
			}
			if (nextOperator != null) {
				iterator.previous();
			}
		}
		iterator.previous();
		OperatorExpression result = new OperatorExpression(tok);
		result.leftOperand = (ExpressionDescriptor)descriptorsStack.removeLast();
		setParent(result.leftOperand, result);
		descriptorsStack.add(result);
	}

	private void parseMethodCall(Token first, ListIterator<Token> iterator,
			Deque<AbstractDescriptor> descriptorsStack, TokenBasedJavaParser parser) throws PreprocessorException {
		String name;
		if (first.equalsTo('<')) {
			int deep = 1;
			while(deep > 0) {
				Token tok = iterator.next();
				if (tok.equalsTo('<')) {
					deep++;
				} else if (tok.equalsTo('>')) {
					deep--;
				} else if (tok.equalsTo(">>")) {
					deep -= 2;
				} else if (tok.equalsTo(">>>")) {
					deep -= 3;
				}
			}
			name = iterator.next().toString();
			Token bracket = iterator.next();
			if (!bracket.equalsTo('(')) {
				throw new PreprocessorParseException(bracket);
			}
		} else {
			name = first.toString();
		}
		
		MethodCallExpression method = new MethodCallExpression(first);
		method.name = name;
		method.arguments = parseArguments(parser, descriptorsStack, iterator, false, null);
		for (ExpressionDescriptor arg : method.arguments) {
			setParent(arg, method);
		}
		if (!descriptorsStack.isEmpty() && !isIncompleteOperator(descriptorsStack.getLast())) {
			method.target = (ExpressionDescriptor) descriptorsStack.removeLast();
			setParent(method.target, method);
		}
		iterator.previous();
		method.setLastToken(iterator.next());
		checkStack(iterator, descriptorsStack, method);
	}

	private void parseSubexprOrCast(Token first, Deque<AbstractDescriptor> descriptorsStack,
			ListIterator<Token> iterator, TokenBasedJavaParser parser)
			throws PreprocessorException {
		List<Token> tokens = extractSubexpression(iterator, ')', (char)0, false);
		if (!iterator.hasNext()) {
			checkStack(iterator, descriptorsStack, parse(parser, tokens));
			return;
		}
		Token tok = iterator.next();
		iterator.previous();
		
		if ((!tok.isWord() || tok.equalsTo(JavaKeywords.INSTANCEOF)) && 
				!tok.isCharacterLiteral() && !tok.isDigitLiteral() && 
				!tok.isStringLiteral() && !tok.equalsTo('(')) {
			checkStack(iterator, descriptorsStack, parse(parser, tokens));
			return;
		}
		
		CastExpression cast = new CastExpression(first);
		StringBuilder type = new StringBuilder();
		TokenSource source = new IteratorTokenSource(tokens.iterator());
		source.next();
		DescriptorParserUtils.readTypeName(type, source, true);
		cast.type = type.toString();
		descriptorsStack.add(cast);
	}

	private void parseNewExpression(Token first, TokenBasedJavaParser parser, Deque<AbstractDescriptor> descriptorsStack, 
			ListIterator<Token> iterator) throws PreprocessorException {
		NewExpression result = new NewExpression(first);
		TokenSource source = new IteratorTokenSource(iterator);
		source.next();
		
		StringBuilder typeName = new StringBuilder();
		DescriptorParserUtils.readTypeName(typeName, source, false);
		result.type = typeName.toString();
		
		result.array = source.current().equalsTo('[');
		
		result.arguments = parseArguments(parser, descriptorsStack, iterator, result.array, source);
		for (ExpressionDescriptor arg : result.arguments) {
			setParent(arg, result);
		}
		result.setLastToken(source.current());
		
		if (source.hasNext() && source.next().equalsTo('{')) {
			result.initializer = parseInitializerOrAnonymousClass(parser, result, source, iterator, descriptorsStack);
			setParent(result.initializer, result);
			result.setLastToken(source.current());
		} 
		if (source.hasNext()) {
			iterator.previous();
		}
		
		checkStack(iterator, descriptorsStack, result);
	}

	private InitializerExpressionDescriptor parseInitializerOrAnonymousClass(TokenBasedJavaParser parser, NewExpression expression,
			TokenSource source, ListIterator<Token> iterator, Deque<AbstractDescriptor> descriptorsStack) throws PreprocessorException {
		if (!expression.array) {
			expression.anonymousClass = (ClassDescriptor)DescriptorType.CLASS.parse(parser, source,
					Collections.emptyList(), ClassDescriptorType.CLASS.name(), expression.type);
			return null;
		}
		if (!source.hasNext()) {
			throw new PreprocessorParseException("Unexpected end of file", source.current());
		}
		return parseInitializer(parser, source, iterator, descriptorsStack);
	}

	private InitializerExpressionDescriptor parseInitializer(
			TokenBasedJavaParser parser,
			TokenSource source, ListIterator<Token> iterator,
			Deque<AbstractDescriptor> descriptorsStack)
			throws PreprocessorException, PreprocessorParseException {
		InitializerExpression result = new InitializerExpression(source.current());
		
		while (!source.next().equalsTo('}')) {
			if (source.current().equalsTo('{')) {
				result.children.add(
						parseInitializer(parser, source, iterator, descriptorsStack));
			} else {
				Token current = source.current();
				List<Token> tokens = extractSubexpression(iterator, ',', (char)0, true);
				if (!current.equalsTo(',')) tokens.add(0, current);
				result.children.add((ExpressionDescriptor) parse(parser, tokens));
				iterator.previous();
			}
		}
		result.setLastToken(source.current());
		if (source.hasNext()) {
			source.next();
		}
		
		return result;
	}

	private List<ExpressionDescriptor> parseArguments(
			TokenBasedJavaParser parser,Deque<AbstractDescriptor> descriptorsStack,
			ListIterator<Token> iterator, boolean isArray,
			TokenSource source) throws PreprocessorException {
		if (isArray) {
			List<ExpressionDescriptor> arguments = new ArrayList<ExpressionDescriptor>(1);
			iterator.previous();
			while (source.hasNext() && source.next().equalsTo('[')) {
				Deque<AbstractDescriptor> subexpressionStack = new LinkedList<AbstractDescriptor>();
				parseSubexpression(parser, subexpressionStack, iterator, ']', '[');
				arguments.add((ExpressionDescriptor)subexpressionStack.removeLast());
			}
			if (source.hasNext()) {
				iterator.previous();
			}
			
			return Collections.unmodifiableList(arguments);
		}
		Deque<AbstractDescriptor> subexpressionStack = new LinkedList<AbstractDescriptor>();
		parseSubexpression(parser, subexpressionStack, iterator, ')', (char)0);
		AbstractDescriptor descriptor = subexpressionStack.removeLast();
		if (descriptor instanceof EmptyExpression) {
			return Collections.emptyList();
		}
		if (descriptor instanceof CommaExpressionDescriptor) {
			return ((CommaExpressionDescriptor)descriptor).getExpressions();
		}
		return Collections.singletonList((ExpressionDescriptor)descriptor);
	}

	private void checkStack(ListIterator<Token> iterator, Deque<AbstractDescriptor> descriptorsStack, AbstractDescriptor descriptor) {
		if (descriptorsStack.isEmpty()) {
			descriptorsStack.add(descriptor);
			return;
		}
		AbstractDescriptor last = descriptorsStack.getLast();
		
		if (last instanceof TernaryOperatorExpression) {
			TernaryOperatorExpression operator = (TernaryOperatorExpression)last;
			if (operator.falseExpression != null) {
				descriptorsStack.add(descriptor);
				return;
			}
			if (!iterator.hasNext()) {
				operator.falseExpression = (ExpressionDescriptor) descriptor;
				setParent(operator.falseExpression, operator);
				if (descriptorsStack.size() > 1) {
					descriptorsStack.removeLast();
					checkStack(iterator, descriptorsStack, operator);
				}
				return;
			}
			
			int thisPriority = OPERATORS_PRIORITIES.get("?");
			
			Token nextOperator = iterator.next();
			iterator.previous();
			Integer nextPriority = OPERATORS_PRIORITIES.get(nextOperator.toString());
			
			if (thisPriority <= nextPriority) {
				operator.falseExpression = (ExpressionDescriptor) descriptor;
				setParent(operator.falseExpression, operator);
				if (descriptorsStack.size() > 1) {
					descriptorsStack.removeLast();
					checkStack(iterator, descriptorsStack, operator);
				}
			} else {
				descriptorsStack.add(descriptor);
			}
			return;
		}
		
		if (!isIncompleteOperator(last)) {
			descriptorsStack.add(descriptor);
			return;
		}
		if (last instanceof CastExpression) {
			CastExpression cast = (CastExpression)last;
			if (!iterator.hasNext()) {
				descriptorsStack.removeLast();
				cast.target = (ExpressionDescriptor) descriptor;
				setParent(cast.target, cast);
				checkStack(iterator, descriptorsStack, cast);
				return;
			}
			Token nextOperator = iterator.next();
			iterator.previous();
			Integer nextPriority = OPERATORS_PRIORITIES.get(nextOperator.toString());
			if (nextPriority == null || nextPriority.intValue() >= 2) {
				cast.target = (ExpressionDescriptor) descriptor;
				setParent(cast.target, cast);
			} else {
				descriptorsStack.add(descriptor);
			}
			return;
		}
		OperatorExpression incomplete = (OperatorExpression)last;
		
		if (!iterator.hasNext()) {
			descriptorsStack.removeLast();
			incomplete.rightOperand = (ExpressionDescriptor) descriptor;
			setParent(incomplete.rightOperand, incomplete);
			checkStack(iterator, descriptorsStack, incomplete);
			return;
		}
		int thisPriority = OPERATORS_PRIORITIES.get(incomplete.operator);
		Token nextOperator = iterator.next();
		iterator.previous();
		Integer nextPriority = OPERATORS_PRIORITIES.get(nextOperator.toString());
		
		if (nextPriority == null || nextPriority.intValue() >= thisPriority) {
			incomplete.rightOperand = (ExpressionDescriptor) descriptor;
			setParent(incomplete.rightOperand, incomplete);
			if (descriptorsStack.size() > 1) {
				checkStack(iterator, descriptorsStack, descriptorsStack.removeLast());
			}
		} else {
			descriptorsStack.add(descriptor);
		}
	}

	private void setParent(ExpressionDescriptor child,
			ExpressionDescriptor parent) {
		if (child instanceof AbstractDescriptor) {
			((AbstractDescriptor)child).setParent(parent);
		}
	}

	private boolean isIncompleteOperator(AbstractDescriptor descriptor) {
		if (descriptor instanceof CastExpression) {
			return ((CastExpression)descriptor).target == null;
		}
		if (descriptor instanceof TernaryOperatorExpression) {
			return ((TernaryOperatorExpression)descriptor).falseExpression == null;
		}
		if (!(descriptor instanceof OperatorExpression)) {
			return false;
		}
		OperatorExpression expression = (OperatorExpression) descriptor;
		return expression.leftOperand == null ||
				(!expression.defined && expression.rightOperand == null);
	}

	private void parseUnary(Deque<AbstractDescriptor> descriptorsStack, Token operator) {
		if (!descriptorsStack.isEmpty() && descriptorsStack.getLast() instanceof OperatorExpression &&
				((OperatorExpression)descriptorsStack.getLast()).getOperator().equals(".") &&
				(operator.equalsTo("++") || operator.equalsTo("--"))) {
			
			OperatorExpression result = new OperatorExpression(operator);
			result.leftOperand = (ExpressionDescriptor)descriptorsStack.removeLast();
			setParent(result.leftOperand, result);
			result.defined = true;
			descriptorsStack.add(result);
			return;
		}
		OperatorExpression result = new OperatorExpression(operator);
		if (isUnaryPrefix(descriptorsStack)) {
			result.leftOperand = (ExpressionDescriptor)descriptorsStack.removeLast();
			setParent(result.leftOperand, result);
			result.defined = true;
		} 
		descriptorsStack.add(result);
	}

	private boolean isUnaryPrefix(Deque<AbstractDescriptor> descriptorsStack) {
		if (descriptorsStack.isEmpty()) {
			return false;
		}
		if (descriptorsStack.getLast() instanceof NameExpression) {
			return true;
		}
		if (!(descriptorsStack.getLast() instanceof OperatorExpression)) {
			return false;
		}
		OperatorExpression operator = (OperatorExpression) descriptorsStack.getLast();
		return operator.operator.equals("[");
	}

	private void parseSubexpression(TokenBasedJavaParser parser, Deque<AbstractDescriptor> descriptorsStack, 
			ListIterator<Token> iterator, char endChar, char deepChar)
			throws PreprocessorException {
		List<Token> tokens = extractSubexpression(iterator, endChar, deepChar, false);
		descriptorsStack.add(parse(parser, tokens));
	}

	private List<Token> extractSubexpression(ListIterator<Token> iterator, 
			char endChar, char deepChar, boolean allowEOF) throws PreprocessorParseException {
		Token tok = iterator.next();
		List<Token> tokens = new LinkedList<Token>();
		int deep = 0;
		while (deep > 0 || !tok.equalsTo(endChar)) {
			if (!iterator.hasNext()) {
				if (allowEOF) {
					return tokens;
				}
				throw new PreprocessorParseException("Syntax error", tok);
			}
			if (tok.equalsTo('(') || tok.equalsTo('{') || 
					(deepChar != 0 && tok.equalsTo(deepChar))) {
				deep++;
			} else if (tok.equalsTo(')') || tok.equalsTo('}') ||
					(deepChar != 0 && tok.equalsTo(endChar))) {
				deep--;
			}
			tokens.add(tok);
			tok = iterator.next();
		}
		return tokens;
	}

	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class NameExpression extends AbstractExpressionDescriptor implements NameExpressionDescriptor {
		
		private String name;
		
		NameExpression(Token first, Token last, String name) {
			super(first, last);
			this.name = name;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.NameExpressionDescriptor#getName()
		 */
		public String getName() {
			return name;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
		}
		
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class OperatorExpression extends AbstractExpressionDescriptor implements OperatorExpressionDescriptor {
		
		private String operator;
		private boolean defined;
		private ExpressionDescriptor leftOperand;
		private ExpressionDescriptor rightOperand;
		private Token token;
		
		OperatorExpression(Token operator) {
			super(null, null);
			this.operator = operator.toString();
			this.token = operator;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.OperatorExpressionDescriptor#getLeftOperand()
		 */
		public ExpressionDescriptor getLeftOperand() {
			return leftOperand;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.OperatorExpressionDescriptor#getRightOperand()
		 */
		public ExpressionDescriptor getRightOperand() {
			return rightOperand;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.OperatorExpressionDescriptor#getOperator()
		 */
		public String getOperator() {
			return operator;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			if (leftOperand != null) leftOperand.visit(visitor);
			if (rightOperand != null) rightOperand.visit(visitor);
		}

		/* (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.impl.AbstractDescriptor#getFirstToken()
		 */
		@Override
		public Token getFirstToken() {
			if (leftOperand == null) {
				return token;
			}
			return leftOperand.getFirstToken();
		}

		/* (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.impl.AbstractDescriptor#getLastToken()
		 */
		@Override
		public Token getLastToken() {
			if (rightOperand == null) {
				return token;
			}
			return ((AbstractDescriptor)rightOperand).getLastToken();
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class TernaryOperatorExpression extends AbstractExpressionDescriptor implements TernaryExpressionDescriptor {

		private ExpressionDescriptor condition;
		private ExpressionDescriptor trueExpression;
		private ExpressionDescriptor falseExpression;
		
		TernaryOperatorExpression() {
			super(null, null);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.TernaryExpressionDescriptor#getCondition()
		 */
		public ExpressionDescriptor getCondition() {
			return condition;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.TernaryExpressionDescriptor#getTrueExpression()
		 */
		public ExpressionDescriptor getExpression() {
			return trueExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.TernaryExpressionDescriptor#getFalseExpression()
		 */
		public ExpressionDescriptor getFalseExpression() {
			return falseExpression;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			condition.visit(visitor);
			trueExpression.visit(visitor);
			falseExpression.visit(visitor);
		}

		/* (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.impl.AbstractDescriptor#getFirstToken()
		 */
		@Override
		public Token getFirstToken() {
			return condition.getFirstToken();
		}

		/* (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.impl.AbstractDescriptor#getLastToken()
		 */
		@Override
		public Token getLastToken() {
			return ((AbstractDescriptor)falseExpression).getLastToken();
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	final static class NewExpression extends AbstractExpressionDescriptor implements NewOperatorExpressionDescriptor {
		
		private List<ExpressionDescriptor> arguments;
		private String type;
		private ClassDescriptor anonymousClass;
		private boolean array;
		private InitializerExpressionDescriptor initializer;
		
		private NewExpression(Token first) {
			super(first, null);
		}
		
		/**
		 * External constructor for enums.
		 * @param first
		 */
		NewExpression(Token first, List<ExpressionDescriptor> arguments, String type, ClassDescriptor anonymousClass) {
			super(first, null);
			this.type = type;
			this.arguments = arguments;
			this.anonymousClass = anonymousClass;
			if (anonymousClass != null) {
				setLastToken(((AbstractDescriptor)anonymousClass).getLastToken());
			} else if (!arguments.isEmpty()) {
				setLastToken(((AbstractDescriptor)arguments.get(arguments.size() - 1)).getLastToken());
			} else {
				setLastToken(first);
			}
		}
		
		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.CallExpressionDescriptor#getArguments()
		 */
		public List<ExpressionDescriptor> getArguments() {
			return arguments;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.NewOperatorExpressionDescriptor#getType()
		 */
		public String getType() {
			if (array) {
				StringBuilder result = new StringBuilder();
				result.append(type);
				for (int i = arguments.size(); i > 0; i--) {
					result.append("[]");
				}
				return result.toString();
			}
			return type;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.NewOperatorExpressionDescriptor#getAnonymousClass()
		 */
		public ClassDescriptor getAnonymousClass() {
			return anonymousClass;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.NewOperatorExpressionDescriptor#isArray()
		 */
		public boolean isArray() {
			return array;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.NewOperatorExpressionDescriptor#getInitializerExpression()
		 */
		public InitializerExpressionDescriptor getInitializerExpression() {
			return initializer;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			for (ExpressionDescriptor arg : arguments) {
				arg.visit(visitor);
			}
			if (anonymousClass != null) {
				anonymousClass.visit(visitor);
			}
			if (initializer != null) {
				initializer.visit(visitor);
			}
		}
	}

	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class InitializerExpression extends AbstractExpressionDescriptor implements InitializerExpressionDescriptor {
		
		InitializerExpression(Token first) {
			super(first, null);
		}

		private List<ExpressionDescriptor> children = new ArrayList<ExpressionDescriptor>(4); 

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.InitializerExpressionDescriptor#getSize()
		 */
		public int getSize() {
			return children.size();
		}

		/*
		 * (non-Javadoc) 
		 * @see org.hawk.gwt.ppc.parser.InitializerExpressionDescriptor#get(int)
		 */
		public Descriptor get(int index) {
			return children.get(index);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			for (Descriptor child : children) {
				child.visit(visitor);
			}
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class CastExpression extends AbstractExpressionDescriptor implements CastExpressionDescriptor {
		
		CastExpression(Token first) {
			super(first, null);
		}

		private String type;
		private ExpressionDescriptor target;

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.CastExpressionDescriptor#getType()
		 */
		public String getType() {
			return type;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.CastExpressionDescriptor#getTarget()
		 */
		public ExpressionDescriptor getTarget() {
			return target;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			target.visit(visitor);
		}

		/* (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.impl.AbstractDescriptor#getLastToken()
		 */
		@Override
		public Token getLastToken() {
			return ((AbstractDescriptor)target).getLastToken();
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private final static class MethodCallExpression extends AbstractExpressionDescriptor implements MethodCallDescriptor {
		
		MethodCallExpression(Token first) {
			super(first, null);
		}

		private List<ExpressionDescriptor> arguments;
		private ExpressionDescriptor target;
		private String name;

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.CallExpressionDescriptor#getArguments()
		 */
		public List<ExpressionDescriptor> getArguments() {
			return arguments;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.MethodCallDescriptor#getTarget()
		 */
		public ExpressionDescriptor getTarget() {
			return target;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.MethodCallDescriptor#getName()
		 */
		public String getName() {
			return name;
		}

		@Override
		public Token getFirstToken() {
			if (target != null) {
				return target.getFirstToken();
			}
			return super.getFirstToken();
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			if (target != null) {
				target.visit(visitor);
			}
			for (Descriptor arg : arguments) {
				arg.visit(visitor);
			}
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private static class CommaExpression extends AbstractExpressionDescriptor implements CommaExpressionDescriptor {

		CommaExpression() {
			super(null, null);
		}

		private List<ExpressionDescriptor> expressions = new ArrayList<ExpressionDescriptor>();

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.CommaExpressionDescriptor#getExpressions()
		 */
		public List<ExpressionDescriptor> getExpressions() {
			return Collections.unmodifiableList(expressions);
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			for (Descriptor child : expressions) {
				child.visit(visitor);
			}
		}

		/* (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.impl.AbstractDescriptor#getFirstToken()
		 */
		@Override
		public Token getFirstToken() {
			if (expressions.isEmpty()) {
				return null;
			}
			return expressions.get(0).getFirstToken();
		}

		/* (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.impl.AbstractDescriptor#getLastToken()
		 */
		@Override
		public Token getLastToken() {
			if (expressions.isEmpty()) {
				return null;
			}
			return expressions.get(expressions.size() - 1).getFirstToken();
		}
	}
}
